using DevExpress.Blazor;
using DevExpress.ExpressApp;
using DevExpress.ExpressApp.Blazor.Services;
using EasyXaf.LowCode.RulesEngineEditors.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using System.Collections.ObjectModel;
using System.Text;

namespace EasyXaf.LowCode.RulesEngineEditors;

public partial class RuleNode : IDisposable
{
    private static readonly Dictionary<string, string>[] Colors = new Dictionary<string, string>[]
    {
        new() { ["BorderColor"] = "#2980B9", ["BackColor"] = "#2980B933" },
        new() { ["BorderColor"] = "#8E44AD", ["BackColor"] = "#8E44AD33" },
        new() { ["BorderColor"] = "#D35400", ["BackColor"] = "#D3540033" },
    };

    private DxContextMenu RuleContextMenu { get; set; }

    private DxContextMenu OperatorContextMenu { get; set; }

    private DxContextMenu NestedRuleOutputModeContextMenu { get; set; }

    private string NodeBackColor => Colors[Rule.Level % 3]["BackColor"];

    private string NodeBorderColor => Colors[Rule.Level % 3]["BorderColor"];

    private string NodeContentStyle
    {
        get
        {
            if (IsStartNode)
            {
                return "background-color:#27AE6033;border-color:#27AE60;";
            }
            else
            {
                return $"background-color:{NodeBackColor};border-color:{NodeBorderColor};";
            }
        }
    }

    private string NodeClassName
    {
        get
        {
            var className = "rule-node ";

            if (!Rule.Enabled)
            {
                className += "disabled-rule ";
            }

            if (Rule.IsFirstRule)
            {
                className += "first-rule ";
            }

            if (Rule.IsLastRule)
            {
                className += "last-rule ";
            }

            if (Rule.Rules.Any())
            {
                className += "has-child-rules ";
            }

            if (Rule.IsCollapse)
            {
                className += "collapse-rule ";
            }

            return className.Trim();
        }
    }

    private string ValidationResultIconColor
    {
        get
        {
            var color = "#27AE60";

            if (Rule.ValidationResults.Any(r => r.Level == ValidationResultLevel.Error))
            {
                color = "#C0392B";
            }
            else if (Rule.ValidationResults.Any(r => r.Level == ValidationResultLevel.Warn))
            {
                color = "#F39C12";
            }

            return color;
        }
    }

    private int RuleIndex => Rules.IndexOf(Rule);

    private bool IsStartNode => Rule is StartObject;

    private bool CanMoveUp => !IsReadOnly && (!Rule.IsFirstRule || (Rule.Parent == null && Rules.IndexOf(Rule) != 1));

    private bool CanMoveDown => !IsReadOnly && !Rule.IsLastRule;

    private bool CanMoveTop => !IsReadOnly && (!Rule.IsFirstRule || (Rule.Parent == null && Rules.IndexOf(Rule) != 1));

    private bool CanMoveBottom => !IsReadOnly && !Rule.IsLastRule;

    private bool CanPaste => !IsReadOnly && Workflow?.LastClonedRule != null;

    private string ValidationResultInfo
    {
        get
        {
            var resultInfo = new StringBuilder();

            foreach (var rule in Rule.ValidationResults)
            {
                resultInfo.AppendLine(rule.Message);
            }

            return resultInfo.ToString();
        }
    }

    private WorkflowObject Workflow => Rule.Workflow;

    private ObservableCollection<RuleObject> Rules => Rule.Parent?.Rules ?? Workflow.Rules;

    private ObservableCollection<RuleObject> ChildRules => Rule.Rules;

    [Parameter]
    public bool IsReadOnly { get; set; }

    [Parameter]
    public RuleObject Rule { get; set; }

    [Parameter]
    public EventCallback RuleChanged { get; set; }

    [Inject]
    private IXafApplicationProvider ApplicationProvider { get; set; }

    private void AddRule(bool before)
    {
        var rule = new RuleObject
        {
            Workflow = Workflow,
            Parent = Rule.Parent
        };

        Rules.Insert(RuleIndex + (before ? 0 : 1), rule);

        rule.Validate();
    }

    private async void AddChildRuleAsync()
    {
        var childRule = new RuleObject
        {
            Workflow = Workflow,
            Parent = Rule
        };

        ChildRules.Add(childRule);

        childRule.Validate();
        Rule.Validate();

        await RuleChanged.InvokeAsync();
    }

    private async Task CollapseChildRulesAsync()
    {
        Rule.IsCollapse = !Rule.IsCollapse;
        await RuleChanged.InvokeAsync();
    }

    private void MoveRule(int index)
    {
        if (Rule.Parent == null && index <= 0)
        {
            index = 1;
        }

        Rules.Move(RuleIndex, index);
    }

    private void CopyRule()
    {
        Workflow.LastClonedRule = Rule.Clone();
    }

    private void CutRule()
    {
        Workflow.LastClonedRule = Rule.Clone();
        DeleteRule();
    }

    private void PasteRule()
    {
        if (Workflow.LastClonedRule != null)
        {
            var rule = Workflow.LastClonedRule.Clone();
            if (IsStartNode)
            {
                Rules.Add(rule);
                rule.Validate();
            }
            else
            {
                rule.Parent = Rule;
                ChildRules.Add(rule);
                rule.Parent.Validate();
            }
        }
    }

    private void DeleteRule()
    {
        Rules.Remove(Rule);

        if (Rule.Parent != null)
        {
            Rule.Parent.Validate();
        }
        else
        {
            Workflow.Validate();
        }
    }

    private void Validate()
    {
        if (IsStartNode)
        {
            Workflow.Validate();
        }
        else
        {
            Rule.Validate();
        }
    }

    private void ChangeRuleOperator(RuleOperator ruleOperator)
    {
        if (Rule.Operator != ruleOperator)
        {
            Rule.Operator = ruleOperator;
            RuleChanged.InvokeAsync();
        }
    }

    private void ChangeNestedRuleOutputMode(NestedRuleOutputMode nestedRuleOutputMode)
    {
        if (Rule.NestedRuleOutputMode != nestedRuleOutputMode)
        {
            Rule.NestedRuleOutputMode = nestedRuleOutputMode;
            RuleChanged.InvokeAsync();
        }
    }

    private void ShowValidationResults()
    {
        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(ValidationResult));

        var collectionSource = new CollectionSource(objectSpace, typeof(ValidationResult));
        foreach (var validationResult in Rule.ValidationResults)
        {
            collectionSource.Add(validationResult);
        }

        var viewId = application.GetListViewId(typeof(ValidationResult));
        var view = application.CreateListView(viewId, collectionSource, true);

        application.ShowViewStrategy.ShowViewInPopupWindow(view);
    }

    private void PopupRulePropertyWindow()
    {
        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(RuleObject));
        var viewId = application.GetDetailViewId(typeof(RuleObject));
        var view = application.CreateDetailView(objectSpace, viewId, true, objectSpace.GetObject(Rule));

        void ObjectSpace_Committed(object sender, EventArgs e)
        {
            RuleChanged.InvokeAsync();
            Rule.Validate();
            InvokeAsync(StateHasChanged);
        }

        application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            objectSpace.SetModified(Rule);
            objectSpace.Committed -= ObjectSpace_Committed;
            objectSpace.Committed += ObjectSpace_Committed;
        });
    }

    private void PopupWorkflowPropertyWindow()
    {
        var application = ApplicationProvider.GetApplication();
        var objectSpace = application.CreateObjectSpace(typeof(WorkflowObject));
        var viewId = application.GetDetailViewId(typeof(WorkflowObject));
        var view = application.CreateDetailView(objectSpace, viewId, true, objectSpace.GetObject(Workflow));

        void ObjectSpace_Committed(object sender, EventArgs e)
        {
            RuleChanged.InvokeAsync();
            Workflow.Validate();
            InvokeAsync(StateHasChanged);
        }

        application.ShowViewStrategy.ShowViewInPopupWindow(view, () =>
        {
            objectSpace.SetModified(Workflow);
            objectSpace.Committed -= ObjectSpace_Committed;
            objectSpace.Committed += ObjectSpace_Committed;
        });
    }

    private void PopupPropertyWindow()
    {
        if (IsStartNode)
        {
            PopupWorkflowPropertyWindow();
        }
        else
        {
            PopupRulePropertyWindow();
        }
    }

    private async Task ShowRuleContextMenuAsync(MouseEventArgs args)
    {
        await RuleContextMenu.ShowAsync(args);
    }

    private async Task ShowOperatorContextMenuAsync(MouseEventArgs args)
    {
        if (!IsReadOnly)
        {
            await OperatorContextMenu.ShowAsync(args);
        }
    }

    private async Task ShowNestedRuleOutputModeContextMenuAsync(MouseEventArgs args)
    {
        if (!IsReadOnly)
        {
            await NestedRuleOutputModeContextMenu.ShowAsync(args);
        }
    }

    private void Rule_ValidationResultsChanged(object sender, EventArgs e)
    {
        InvokeAsync(StateHasChanged);
    }

    protected override void OnInitialized()
    {
        base.OnInitialized();
        Rule.ValidationResultsChanged += Rule_ValidationResultsChanged;
    }

    public void Dispose()
    {
        Rule.ValidationResultsChanged -= Rule_ValidationResultsChanged;
        GC.SuppressFinalize(this);
    }
}